home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / system-config-printer / jobviewer.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2009-10-28  |  48KB  |  1,614 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. import authconn
  5. import cups
  6. import dbus
  7. import dbus.glib as dbus
  8. import dbus.service as dbus
  9. import pynotify
  10. import gettext
  11. import gobject
  12. import gtk
  13. import gtk.gdk as gtk
  14. import gtk.glade as gtk
  15. from glade import GtkGUI
  16. import monitor
  17. import os
  18. import pango
  19. import pwd
  20. import smburi
  21. import subprocess
  22. import sys
  23. import time
  24. import urllib
  25. from debug import *
  26. import config
  27. import statereason
  28. import errordialogs
  29. import pprint
  30.  
  31. try:
  32.     import gnomekeyring
  33.     USE_KEYRING = True
  34. except ImportError:
  35.     USE_KEYRING = False
  36.  
  37. from gettext import gettext as _
  38. DOMAIN = 'system-config-printer'
  39. gettext.textdomain(DOMAIN)
  40. gtk.glade.textdomain(DOMAIN)
  41. gtk.glade.bindtextdomain(DOMAIN)
  42. from statereason import StateReason
  43. statereason.set_gettext_function(_)
  44. errordialogs.set_gettext_function(_)
  45. pkgdata = config.pkgdatadir
  46. GLADE = 'applet.glade'
  47. ICON = 'printer'
  48. SEARCHING_ICON = 'document-print-preview'
  49. pynotify.init('System Config Printer Notification')
  50.  
  51. class PrinterURIIndex:
  52.     
  53.     def __init__(self, names = None):
  54.         self.printer = { }
  55.         self.names = names
  56.  
  57.     
  58.     def update_from_attrs(self, printer, attrs):
  59.         uris = []
  60.         if attrs.has_key('printer-uri-supported'):
  61.             uri_supported = attrs['printer-uri-supported']
  62.             if type(uri_supported) != list:
  63.                 uri_supported = [
  64.                     uri_supported]
  65.             
  66.             uris.extend(uri_supported)
  67.         
  68.         if attrs.has_key('notify-printer-uri'):
  69.             uris.append(attrs['notify-printer-uri'])
  70.         
  71.         if attrs.has_key('printer-more-info'):
  72.             uris.append(attrs['printer-more-info'])
  73.         
  74.         for uri in uris:
  75.             self.printer[uri] = printer
  76.         
  77.  
  78.     
  79.     def remove_printer(self, printer):
  80.         uris = self.printer.keys()
  81.         for uri in uris:
  82.             if self.printer[uri] == printer:
  83.                 del self.printer[uri]
  84.                 continue
  85.         
  86.  
  87.     
  88.     def lookup(self, uri, connection = None):
  89.         
  90.         try:
  91.             return self.printer[uri]
  92.         except KeyError:
  93.             if connection == None:
  94.                 connection = cups.Connection()
  95.             
  96.             r = [
  97.                 'printer-name',
  98.                 'printer-uri-supported',
  99.                 'printer-more-info']
  100.             
  101.             try:
  102.                 attrs = connection.getPrinterAttributes(uri = uri, requested_attributes = r)
  103.             except cups.IPPError:
  104.                 raise KeyError
  105.  
  106.             name = attrs['printer-name']
  107.             self.update_from_attrs(name, attrs)
  108.             self.printer[uri] = name
  109.             
  110.             try:
  111.                 return self.printer[uri]
  112.             except KeyError:
  113.                 pass
  114.             except:
  115.                 None<EXCEPTION MATCH>KeyError
  116.             
  117.  
  118.             None<EXCEPTION MATCH>KeyError
  119.  
  120.         raise KeyError
  121.  
  122.  
  123.  
  124. class JobViewer(GtkGUI, monitor.Watcher):
  125.     required_job_attributes = set([
  126.         'job-k-octets',
  127.         'job-name',
  128.         'job-originating-user-name',
  129.         'job-printer-uri',
  130.         'job-state',
  131.         'time-at-creation'])
  132.     
  133.     def __init__(self, bus = None, loop = None, service_running = False, trayicon = False, suppress_icon_hide = False, my_jobs = True, specific_dests = None, exit_handler = None, parent = None):
  134.         self.loop = loop
  135.         self.service_running = service_running
  136.         self.trayicon = trayicon
  137.         self.suppress_icon_hide = suppress_icon_hide
  138.         self.my_jobs = my_jobs
  139.         self.specific_dests = specific_dests
  140.         self.exit_handler = exit_handler
  141.         self.jobs = { }
  142.         self.jobiters = { }
  143.         self.active_jobs = set()
  144.         self.stopped_job_prompts = set()
  145.         self.printer_state_reasons = { }
  146.         self.num_jobs_when_hidden = 0
  147.         self.connecting_to_device = { }
  148.         self.state_reason_notifications = { }
  149.         self.auth_info_dialogs = { }
  150.         self.job_creation_times_timer = None
  151.         self.special_status_icon = False
  152.         self.new_printer_notifications = { }
  153.         self.authenticated_jobs = set()
  154.         self.getWidgets({
  155.             'JobsWindow': [
  156.                 'JobsWindow',
  157.                 'job_menubar_item',
  158.                 'treeview',
  159.                 'statusbar'],
  160.             'statusicon_popupmenu': [
  161.                 'statusicon_popupmenu'] })
  162.         job_action_group = gtk.ActionGroup('JobActionGroup')
  163.         job_action_group.add_actions([
  164.             ('cancel-job', gtk.STOCK_CANCEL, None, None, None, self.on_job_cancel_activate),
  165.             ('hold-job', gtk.STOCK_MEDIA_PAUSE, _('_Hold'), None, None, self.on_job_hold_activate),
  166.             ('release-job', gtk.STOCK_MEDIA_PLAY, _('_Release'), None, None, self.on_job_release_activate),
  167.             ('reprint-job', gtk.STOCK_REDO, _('Re_print'), None, None, self.on_job_reprint_activate),
  168.             ('authenticate-job', None, _('_Authenticate'), None, None, self.on_job_authenticate_activate)])
  169.         self.job_ui_manager = gtk.UIManager()
  170.         self.job_ui_manager.insert_action_group(job_action_group, -1)
  171.         self.job_ui_manager.add_ui_from_string('\n<ui>\n <accelerator action="cancel-job"/>\n <accelerator action="hold-job"/>\n <accelerator action="release-job"/>\n <accelerator action="reprint-job"/>\n <accelerator action="authenticate-job"/>\n</ui>\n')
  172.         self.job_ui_manager.ensure_update()
  173.         self.JobsWindow.add_accel_group(self.job_ui_manager.get_accel_group())
  174.         self.job_context_menu = gtk.Menu()
  175.         for action_name in [
  176.             'cancel-job',
  177.             'hold-job',
  178.             'release-job',
  179.             'reprint-job',
  180.             None,
  181.             'authenticate-job']:
  182.             if not action_name:
  183.                 item = gtk.SeparatorMenuItem()
  184.             else:
  185.                 action = job_action_group.get_action(action_name)
  186.                 action.set_sensitive(False)
  187.                 item = action.create_menu_item()
  188.             item.show()
  189.             self.job_context_menu.append(item)
  190.         
  191.         self.job_menubar_item.set_submenu(self.job_context_menu)
  192.         for skip, ellipsize, name, setter in [
  193.             (False, False, _('Job'), self._set_job_job_number_text),
  194.             (True, False, _('User'), self._set_job_user_text),
  195.             (False, True, _('Document'), self._set_job_document_text),
  196.             (False, True, _('Printer'), self._set_job_printer_text),
  197.             (False, False, _('Size'), self._set_job_size_text)]:
  198.             if trayicon and skip:
  199.                 continue
  200.             
  201.             cell = gtk.CellRendererText()
  202.             if ellipsize:
  203.                 cell.set_property('ellipsize', pango.ELLIPSIZE_END)
  204.                 cell.set_property('width-chars', 20)
  205.             
  206.             column = gtk.TreeViewColumn(name, cell)
  207.             column.set_cell_data_func(cell, setter)
  208.             column.set_resizable(True)
  209.             self.treeview.append_column(column)
  210.         
  211.         cell = gtk.CellRendererText()
  212.         column = gtk.TreeViewColumn(_('Time submitted'), cell, text = 1)
  213.         column.set_resizable(True)
  214.         self.treeview.append_column(column)
  215.         column = gtk.TreeViewColumn(_('Status'))
  216.         icon = gtk.CellRendererPixbuf()
  217.         column.pack_start(icon, False)
  218.         text = gtk.CellRendererText()
  219.         text.set_property('ellipsize', pango.ELLIPSIZE_END)
  220.         text.set_property('width-chars', 20)
  221.         column.pack_start(text, True)
  222.         column.set_cell_data_func(icon, self._set_job_status_icon)
  223.         column.set_cell_data_func(text, self._set_job_status_text)
  224.         self.treeview.append_column(column)
  225.         self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
  226.         self.store = gtk.TreeStore(int, str)
  227.         self.store.set_sort_column_id(0, gtk.SORT_DESCENDING)
  228.         self.treeview.set_model(self.store)
  229.         self.treeview.set_rules_hint(True)
  230.         self.treeview.connect('button_release_event', self.on_treeview_button_release_event)
  231.         self.treeview.connect('popup-menu', self.on_treeview_popup_menu)
  232.         self.treeview.connect('cursor-changed', self.on_treeview_cursor_changed)
  233.         self.store.connect('row-changed', self.on_treemodel_row_changed)
  234.         self.JobsWindow.set_icon_name(ICON)
  235.         self.JobsWindow.hide()
  236.         if specific_dests:
  237.             the_dests = reduce((lambda x, y: x + ', ' + y), specific_dests)
  238.         
  239.         if my_jobs:
  240.             if specific_dests:
  241.                 title = _('my jobs on %s') % the_dests
  242.             else:
  243.                 title = _('my jobs')
  244.         elif specific_dests:
  245.             title = '%s' % the_dests
  246.         else:
  247.             title = _('all jobs')
  248.         self.JobsWindow.set_title(_('Document Print Status (%s)') % title)
  249.         if parent:
  250.             self.JobsWindow.set_transient_for(parent)
  251.         
  252.         self.statusbar_set = False
  253.         theme = gtk.icon_theme_get_default()
  254.         self.icon_jobs = theme.load_icon(ICON, 22, 0)
  255.         self.icon_jobs_processing = theme.load_icon('printer-printing', 22, 0)
  256.         self.icon_no_jobs = self.icon_jobs.copy()
  257.         self.icon_no_jobs.fill(0)
  258.         self.icon_jobs.composite(self.icon_no_jobs, 0, 0, self.icon_no_jobs.get_width(), self.icon_no_jobs.get_height(), 0, 0, 1, 1, gtk.gdk.INTERP_BILINEAR, 127)
  259.         if self.trayicon:
  260.             self.statusicon = gtk.StatusIcon()
  261.             pixbuf = theme.load_icon(ICON, 22, 0)
  262.             self.statusicon.set_from_pixbuf(pixbuf)
  263.             self.set_statusicon_from_pixbuf(self.icon_no_jobs)
  264.             self.statusicon.connect('activate', self.toggle_window_display)
  265.             self.statusicon.connect('popup-menu', self.on_icon_popupmenu)
  266.             self.statusicon.set_visible(False)
  267.         
  268.         if bus == None:
  269.             bus = dbus.SystemBus()
  270.         
  271.         self.set_process_pending(True)
  272.         self.host = cups.getServer()
  273.         self.port = cups.getPort()
  274.         self.encryption = cups.getEncryption()
  275.         self.monitor = monitor.Monitor(self, bus = bus, my_jobs = my_jobs, specific_dests = specific_dests, host = self.host, port = self.port, encryption = self.encryption)
  276.         if not self.trayicon:
  277.             self.JobsWindow.show()
  278.         
  279.  
  280.     
  281.     def cleanup(self):
  282.         self.monitor.cleanup()
  283.         for l in [
  284.             self.new_printer_notifications.values(),
  285.             self.state_reason_notifications.values()]:
  286.             for notification in l:
  287.                 if notification.get_data('closed') != True:
  288.                     notification.close()
  289.                     notification.set_data('closed', True)
  290.                     continue
  291.             
  292.         
  293.         if self.job_creation_times_timer != None:
  294.             gobject.source_remove(self.job_creation_times_timer)
  295.             self.job_creation_times_timer = None
  296.         
  297.         if self.exit_handler:
  298.             self.exit_handler(self)
  299.         
  300.  
  301.     
  302.     def set_process_pending(self, whether):
  303.         self.process_pending_events = whether
  304.  
  305.     
  306.     def set_special_statusicon(self, iconname, tooltip = None):
  307.         self.special_status_icon = True
  308.         self.statusicon.set_from_icon_name(iconname)
  309.         self.set_statusicon_visibility()
  310.         if tooltip != None:
  311.             self.set_statusicon_tooltip(tooltip = tooltip)
  312.         
  313.  
  314.     
  315.     def unset_special_statusicon(self):
  316.         self.special_status_icon = False
  317.         self.statusicon.set_from_pixbuf(self.saved_statusicon_pixbuf)
  318.         self.set_statusicon_visibility()
  319.         self.set_statusicon_tooltip()
  320.  
  321.     
  322.     def notify_new_printer(self, printer, notification):
  323.         self.new_printer_notifications[printer] = notification
  324.         notification.set_data('printer-name', printer)
  325.         notification.connect('closed', self.on_new_printer_notification_closed)
  326.         self.set_statusicon_visibility()
  327.         notification.attach_to_status_icon(self.statusicon)
  328.         
  329.         try:
  330.             notification.show()
  331.         except gobject.GError:
  332.             nonfatalException()
  333.  
  334.  
  335.     
  336.     def on_new_printer_notification_closed(self, notification, reason = None):
  337.         printer = notification.get_data('printer-name')
  338.         del self.new_printer_notifications[printer]
  339.         self.set_statusicon_visibility()
  340.  
  341.     
  342.     def set_statusicon_from_pixbuf(self, pb):
  343.         self.saved_statusicon_pixbuf = pb
  344.         if not self.special_status_icon:
  345.             self.statusicon.set_from_pixbuf(pb)
  346.         
  347.  
  348.     
  349.     def on_delete_event(self, *args):
  350.         if self.trayicon or not (self.loop):
  351.             self.JobsWindow.hide()
  352.             if not self.loop:
  353.                 self.cleanup()
  354.             
  355.         else:
  356.             self.loop.quit()
  357.         return True
  358.  
  359.     
  360.     def show_IPP_Error(self, exception, message):
  361.         return errordialogs.show_IPP_Error(exception, message, self.JobsWindow)
  362.  
  363.     
  364.     def toggle_window_display(self, icon, force_show = False):
  365.         visible = self.JobsWindow.get_property('visible')
  366.         if force_show:
  367.             visible = False
  368.         
  369.         if visible:
  370.             self.JobsWindow.hide()
  371.         else:
  372.             self.JobsWindow.show()
  373.  
  374.     
  375.     def on_show_completed_jobs_activate(self, menuitem):
  376.         if menuitem.get_active():
  377.             which_jobs = 'all'
  378.         else:
  379.             which_jobs = 'not-completed'
  380.         self.monitor.refresh(which_jobs = which_jobs, refresh_all = False)
  381.  
  382.     
  383.     def update_job_creation_times(self):
  384.         now = time.time()
  385.         need_update = False
  386.         for job, data in self.jobs.iteritems():
  387.             if self.jobs.has_key(job):
  388.                 iter = self.jobiters[job]
  389.             
  390.             t = _('Unknown')
  391.             if data.has_key('time-at-creation'):
  392.                 created = data['time-at-creation']
  393.                 ago = now - created
  394.                 need_update = True
  395.                 if ago < 120:
  396.                     t = _('a minute ago')
  397.                 elif ago < 3600:
  398.                     mins = int(ago / 60)
  399.                     t = _('%d minutes ago') % mins
  400.                 elif ago < 86400:
  401.                     hours = int(ago / 3600)
  402.                     if hours == 1:
  403.                         t = _('an hour ago')
  404.                     else:
  405.                         t = _('%d hours ago') % hours
  406.                 elif ago < 604800:
  407.                     days = int(ago / 86400)
  408.                     if days == 1:
  409.                         t = _('yesterday')
  410.                     else:
  411.                         t = _('%d days ago') % days
  412.                 elif ago < 3628800:
  413.                     weeks = int(ago / 604800)
  414.                     if weeks == 1:
  415.                         t = _('last week')
  416.                     else:
  417.                         t = _('%d weeks ago') % weeks
  418.                 else:
  419.                     need_update = False
  420.                     t = time.strftime('%B %Y', time.localtime(created))
  421.             
  422.             self.store.set_value(iter, 1, t)
  423.         
  424.         if need_update and not (self.job_creation_times_timer):
  425.             t = gobject.timeout_add(60000, self.update_job_creation_times)
  426.             self.job_creation_times_timer = t
  427.         
  428.         if not need_update:
  429.             if self.job_creation_times_timer:
  430.                 gobject.source_remove(self.job_creation_times_timer)
  431.                 self.job_creation_times_timer = None
  432.             
  433.         
  434.         return need_update
  435.  
  436.     
  437.     def print_error_dialog_response(self, dialog, response, jobid):
  438.         dialog.hide()
  439.         dialog.destroy()
  440.         self.stopped_job_prompts.remove(jobid)
  441.         if response == gtk.RESPONSE_NO:
  442.             if not self.__dict__.has_key('troubleshooter'):
  443.                 import troubleshoot as troubleshoot
  444.                 troubleshooter = troubleshoot.run(self.on_troubleshoot_quit)
  445.                 self.troubleshooter = troubleshooter
  446.             
  447.         
  448.  
  449.     
  450.     def on_troubleshoot_quit(self, troubleshooter):
  451.         del self.troubleshooter
  452.  
  453.     
  454.     def add_job(self, job, data, connection = None):
  455.         self.update_job(job, data, connection = connection)
  456.         store = self.store
  457.         iter = self.store.append(None)
  458.         store.set_value(iter, 0, job)
  459.         debugprint('Job %d added' % job)
  460.         self.jobiters[job] = iter
  461.         range = self.treeview.get_visible_range()
  462.         if range != None:
  463.             (start, end) = range
  464.             if self.store.get_sort_column_id() == (0, gtk.SORT_DESCENDING) and start == (1,):
  465.                 self.treeview.scroll_to_cell((0,), None, False, 0, 0)
  466.             
  467.         
  468.         if not self.job_creation_times_timer:
  469.             
  470.             def start_updating_job_creation_times():
  471.                 self.update_job_creation_times()
  472.                 return False
  473.  
  474.             gobject.timeout_add(500, start_updating_job_creation_times)
  475.         
  476.  
  477.     
  478.     def update_job(self, job, data, connection = None):
  479.         r = self.required_job_attributes - set(data.keys())
  480.         if r:
  481.             attrs = None
  482.             
  483.             try:
  484.                 if connection == None:
  485.                     connection = cups.Connection(host = self.host, port = self.port, encryption = self.encryption)
  486.                 
  487.                 debugprint('requesting %s' % r)
  488.                 r = list(r)
  489.                 attrs = connection.getJobAttributes(job, requested_attributes = r)
  490.             except RuntimeError:
  491.                 pass
  492.             except AttributeError:
  493.                 pass
  494.  
  495.             if attrs:
  496.                 data.update(attrs)
  497.             
  498.         
  499.         self.jobs[job] = data
  500.         job_requires_auth = False
  501.         
  502.         try:
  503.             jstate = data.get('job-state', cups.IPP_JOB_PROCESSING)
  504.             s = int(jstate)
  505.             if s in [
  506.                 cups.IPP_JOB_HELD,
  507.                 cups.IPP_JOB_STOPPED]:
  508.                 jattrs = [
  509.                     'job-state',
  510.                     'job-hold-until']
  511.                 pattrs = [
  512.                     'auth-info-required',
  513.                     'device-uri']
  514.                 uri = data.get('job-printer-uri')
  515.                 c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  516.                 attrs = c.getPrinterAttributes(uri = uri, requested_attributes = pattrs)
  517.                 
  518.                 try:
  519.                     auth_info_required = attrs['auth-info-required']
  520.                 except KeyError:
  521.                     debugprint('No auth-info-required attribute; guessing instead')
  522.                     auth_info_required = [
  523.                         'username',
  524.                         'password']
  525.  
  526.                 if not isinstance(auth_info_required, list):
  527.                     auth_info_required = [
  528.                         auth_info_required]
  529.                     attrs['auth-info-required'] = auth_info_required
  530.                 
  531.                 data.update(attrs)
  532.                 attrs = c.getJobAttributes(job, requested_attributes = jattrs)
  533.                 data.update(attrs)
  534.                 jstate = data.get('job-state', cups.IPP_JOB_PROCESSING)
  535.                 s = int(jstate)
  536.         except ValueError:
  537.             pass
  538.         except RuntimeError:
  539.             pass
  540.         except cups.IPPError:
  541.             (e, m) = None
  542.  
  543.         
  544.         try:
  545.             del data['_status_text']
  546.         except KeyError:
  547.             pass
  548.  
  549.         self.treeview.queue_draw()
  550.         if self.trayicon:
  551.             if s == cups.IPP_JOB_HELD:
  552.                 pass
  553.             job_requires_auth = data.get('job-hold-until', 'none') == 'auth-info-required'
  554.             if job_requires_auth and not self.auth_info_dialogs.has_key(job):
  555.                 
  556.                 try:
  557.                     cups.require('1.9.37')
  558.                 except:
  559.                     debugprint('Authentication required but authenticateJob() not available')
  560.                     return None
  561.  
  562.                 try_keyring = USE_KEYRING
  563.                 keyring_attrs = None
  564.                 auth_info = None
  565.                 if try_keyring and 'password' in auth_info_required:
  566.                     auth_info_required = data.get('auth-info-required', [])
  567.                     device_uri = data.get('device-uri')
  568.                     (scheme, rest) = urllib.splittype(device_uri)
  569.                     keyring_attrs = dict()
  570.                     if scheme == 'smb':
  571.                         uri = smburi.SMBURI(uri = device_uri)
  572.                         (group, server, share, user, password) = uri.separate()
  573.                         keyring_attrs['domain'] = str(group)
  574.                     else:
  575.                         (serverport, rest) = urllib.splithost(rest)
  576.                         (server, port) = urllib.splitnport(serverport)
  577.                     username = pwd.getpwuid(os.getuid())[0]
  578.                     keyring_attrs.update({
  579.                         'server': str(server.lower()),
  580.                         'protocol': str(scheme),
  581.                         'user': str(username) })
  582.                 
  583.                 if job in self.authenticated_jobs:
  584.                     try_keyring = False
  585.                 
  586.                 if try_keyring and 'password' in auth_info_required:
  587.                     type = gnomekeyring.ITEM_NETWORK_PASSWORD
  588.                     
  589.                     try:
  590.                         items = gnomekeyring.find_items_sync(type, keyring_attrs)
  591.                         auth_info = map((lambda x: ''), auth_info_required)
  592.                         ind = auth_info_required.index('username')
  593.                         auth_info[ind] = items[0].attributes.get('user', '')
  594.                         ind = auth_info_required.index('password')
  595.                         auth_info[ind] = items[0].secret
  596.                     except gnomekeyring.NoMatchError:
  597.                         debugprint('gnomekeyring: no match for %s' % keyring_attrs)
  598.                     except gnomekeyring.DeniedError:
  599.                         debugprint('gnomekeyring: denied for %s' % keyring_attrs)
  600.                     except:
  601.                         None<EXCEPTION MATCH>gnomekeyring.NoMatchError
  602.                     
  603.  
  604.                 None<EXCEPTION MATCH>gnomekeyring.NoMatchError
  605.                 if try_keyring and c == None:
  606.                     
  607.                     try:
  608.                         c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  609.                     except RuntimeError:
  610.                         try_keyring = False
  611.                     except:
  612.                         None<EXCEPTION MATCH>RuntimeError
  613.                     
  614.  
  615.                 None<EXCEPTION MATCH>RuntimeError
  616.                 if try_keyring and auth_info != None:
  617.                     
  618.                     try:
  619.                         c._begin_operation(_('authenticating job'))
  620.                         c.authenticateJob(job, auth_info)
  621.                         c._end_operation()
  622.                         self.monitor.update()
  623.                         debugprint('Automatically authenticated job %d' % job)
  624.                         self.authenticated_jobs.add(job)
  625.                         return None
  626.                     except cups.IPPError:
  627.                         (e, m) = None
  628.                         c._end_operation()
  629.                         nonfatalException()
  630.                         return None
  631.                         c._end_operation()
  632.                         nonfatalException()
  633.                     
  634.  
  635.                 None<EXCEPTION MATCH>cups.IPPError
  636.                 self.display_auth_info_dialog(job)
  637.             
  638.         
  639.  
  640.     
  641.     def on_auth_notification_closed(self, notification, reason = None):
  642.         job = notification.get_data('job-id')
  643.         debugprint('auth notification closed for job %s' % job)
  644.         self.auth_notifications[job].set_data('closed', True)
  645.         del self.auth_notifications[job]
  646.  
  647.     
  648.     def on_auth_notification_authenticate(self, notification, action):
  649.         job = notification.get_data('job-id')
  650.         keyring_attrs = notification.get_data('keyring-attrs')
  651.         debugprint('auth notification authenticate for job %s' % job)
  652.         self.display_auth_info_dialog(job, keyring_attrs)
  653.  
  654.     
  655.     def display_auth_info_dialog(self, job, keyring_attrs = None):
  656.         data = self.jobs[job]
  657.         auth_info_required = data['auth-info-required']
  658.         dialog = authconn.AuthDialog(auth_info_required = auth_info_required, allow_remember = USE_KEYRING)
  659.         dialog.set_data('keyring-attrs', keyring_attrs)
  660.         dialog.set_data('auth-info-required', auth_info_required)
  661.         dialog.set_position(gtk.WIN_POS_CENTER)
  662.         auth_info = map((lambda x: ''), auth_info_required)
  663.         username = pwd.getpwuid(os.getuid())[0]
  664.         if 'username' in auth_info_required:
  665.             
  666.             try:
  667.                 ind = auth_info_required.index('username')
  668.                 auth_info[ind] = username
  669.                 dialog.set_auth_info(auth_info)
  670.             nonfatalException()
  671.  
  672.         
  673.         index = 0
  674.         for field in auth_info_required:
  675.             if auth_info[index] == '':
  676.                 dialog.field_grab_focus(field)
  677.                 break
  678.             
  679.             index += 1
  680.         
  681.         dialog.set_prompt(_("Authentication required for printing document `%s' (job %d)") % (data.get('job-name', _('Unknown')), job))
  682.         self.auth_info_dialogs[job] = dialog
  683.         dialog.connect('response', self.auth_info_dialog_response)
  684.         dialog.connect('delete-event', self.auth_info_dialog_delete)
  685.         dialog.set_data('job-id', job)
  686.         dialog.show_all()
  687.         dialog.set_keep_above(True)
  688.         dialog.show_now()
  689.  
  690.     
  691.     def auth_info_dialog_delete(self, dialog, event):
  692.         self.auth_info_dialog_response(dialog, gtk.RESPONSE_CANCEL)
  693.  
  694.     
  695.     def auth_info_dialog_response(self, dialog, response):
  696.         jobid = dialog.get_data('job-id')
  697.         del self.auth_info_dialogs[jobid]
  698.         if response != gtk.RESPONSE_OK:
  699.             dialog.destroy()
  700.             return None
  701.         auth_info = dialog.get_auth_info()
  702.         
  703.         try:
  704.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  705.         except RuntimeError:
  706.             response != gtk.RESPONSE_OK
  707.             response != gtk.RESPONSE_OK
  708.             debugprint('Error connecting to CUPS for authentication')
  709.             return None
  710.  
  711.         remember = False
  712.         c._begin_operation(_('authenticating job'))
  713.         
  714.         try:
  715.             c.authenticateJob(jobid, auth_info)
  716.             remember = dialog.get_remember_password()
  717.             self.authenticated_jobs.add(jobid)
  718.             self.monitor.update()
  719.         except cups.IPPError:
  720.             response != gtk.RESPONSE_OK
  721.             (e, m) = response != gtk.RESPONSE_OK
  722.             self.show_IPP_Error(e, m)
  723.         except:
  724.             response != gtk.RESPONSE_OK
  725.  
  726.         c._end_operation()
  727.         dialog.destroy()
  728.  
  729.     
  730.     def set_statusicon_visibility(self):
  731.         if not self.trayicon:
  732.             return None
  733.         if self.suppress_icon_hide:
  734.             self.suppress_icon_hide = False
  735.             return None
  736.         open_notifications = len(self.new_printer_notifications.keys())
  737.         for reason, notification in self.state_reason_notifications.iteritems():
  738.             if notification.get_data('closed') != True:
  739.                 open_notifications += 1
  740.                 continue
  741.             self.suppress_icon_hide
  742.         
  743.         num_jobs = len(self.active_jobs)
  744.         debugprint('open notifications: %d' % open_notifications)
  745.         debugprint('num_jobs: %d' % num_jobs)
  746.         debugprint('num_jobs_when_hidden: %d' % self.num_jobs_when_hidden)
  747.         if not self.special_status_icon and open_notifications > 0:
  748.             pass
  749.         self.statusicon.set_visible(num_jobs > self.num_jobs_when_hidden)
  750.         while self.process_pending_events and gtk.events_pending():
  751.             gtk.main_iteration()
  752.             continue
  753.             self.trayicon
  754.  
  755.     
  756.     def on_treeview_popup_menu(self, treeview):
  757.         event = gtk.gdk.Event(gtk.gdk.NOTHING)
  758.         self.show_treeview_popup_menu(treeview, event, 0)
  759.  
  760.     
  761.     def on_treeview_button_release_event(self, treeview, event):
  762.         if event.button == 3:
  763.             self.show_treeview_popup_menu(treeview, event, event.button)
  764.         
  765.  
  766.     
  767.     def on_treemodel_row_changed(self, model, path, iter):
  768.         self.on_treeview_cursor_changed(self.treeview)
  769.  
  770.     
  771.     def on_treeview_cursor_changed(self, treeview):
  772.         (path, column) = treeview.get_cursor()
  773.         cancel = self.job_ui_manager.get_action('/cancel-job')
  774.         hold = self.job_ui_manager.get_action('/hold-job')
  775.         release = self.job_ui_manager.get_action('/release-job')
  776.         reprint = self.job_ui_manager.get_action('/reprint-job')
  777.         authenticate = self.job_ui_manager.get_action('/authenticate-job')
  778.         if path == None:
  779.             for widget in [
  780.                 cancel,
  781.                 hold,
  782.                 release,
  783.                 reprint,
  784.                 authenticate]:
  785.                 widget.set_sensitive(False)
  786.             
  787.             return None
  788.         iter = self.store.get_iter(path)
  789.         self.jobid = self.store.get_value(iter, 0)
  790.         job = self.jobs[self.jobid]
  791.         authenticate.set_sensitive(False)
  792.         for widget in [
  793.             cancel,
  794.             hold,
  795.             release,
  796.             reprint]:
  797.             widget.set_sensitive(True)
  798.         
  799.         if job.has_key('job-state'):
  800.             s = job['job-state']
  801.             if s >= cups.IPP_JOB_CANCELED:
  802.                 cancel.set_sensitive(False)
  803.             
  804.             if s != cups.IPP_JOB_PENDING:
  805.                 hold.set_sensitive(False)
  806.             
  807.             if s != cups.IPP_JOB_HELD:
  808.                 release.set_sensitive(False)
  809.             
  810.             if not job.get('job-preserved', False):
  811.                 reprint.set_sensitive(False)
  812.             
  813.         
  814.         if job.get('job-state', cups.IPP_JOB_CANCELED) == cups.IPP_JOB_HELD:
  815.             if job.get('job-hold-until', 'none') == 'auth-info-required':
  816.                 authenticate.set_sensitive(True)
  817.             
  818.         
  819.  
  820.     
  821.     def show_treeview_popup_menu(self, treeview, event, event_button):
  822.         self.job_context_menu.popup(None, None, None, event_button, event.get_time())
  823.  
  824.     
  825.     def on_icon_popupmenu(self, icon, button, time):
  826.         self.statusicon_popupmenu.popup(None, None, None, button, time)
  827.  
  828.     
  829.     def on_icon_hide_activate(self, menuitem):
  830.         self.num_jobs_when_hidden = len(self.jobs.keys())
  831.         self.set_statusicon_visibility()
  832.  
  833.     
  834.     def on_icon_configure_printers_activate(self, menuitem):
  835.         if self.loop:
  836.             env = { }
  837.             for name, value in os.environ.iteritems():
  838.                 if name == 'SYSTEM_CONFIG_PRINTER_GLADE':
  839.                     continue
  840.                 
  841.                 env[name] = value
  842.             
  843.             p = subprocess.Popen([
  844.                 'system-config-printer'], close_fds = True, env = env)
  845.             gobject.timeout_add(10000, self.poll_subprocess, p)
  846.         
  847.  
  848.     
  849.     def poll_subprocess(self, process):
  850.         returncode = process.poll()
  851.         return returncode == None
  852.  
  853.     
  854.     def on_icon_quit_activate(self, menuitem):
  855.         if self.loop:
  856.             self.loop.quit()
  857.         
  858.  
  859.     
  860.     def on_job_cancel_activate(self, menuitem):
  861.         dialog = gtk.Dialog(_('Cancel Job'), self.JobsWindow, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, (gtk.STOCK_NO, gtk.RESPONSE_NO, gtk.STOCK_YES, gtk.RESPONSE_YES))
  862.         dialog.set_default_response(gtk.RESPONSE_NO)
  863.         dialog.set_border_width(6)
  864.         dialog.set_resizable(False)
  865.         hbox = gtk.HBox(False, 12)
  866.         image = gtk.Image()
  867.         image.set_from_stock(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
  868.         image.set_alignment(0, 0)
  869.         hbox.pack_start(image, False, False, 0)
  870.         label = gtk.Label(_('Do you really want to cancel this job?'))
  871.         label.set_line_wrap(True)
  872.         label.set_alignment(0, 0)
  873.         hbox.pack_start(label, False, False, 0)
  874.         dialog.vbox.pack_start(hbox, False, False, 0)
  875.         dialog.set_data('job-id', self.jobid)
  876.         dialog.connect('response', self.on_job_cancel_prompt_response)
  877.         dialog.connect('delete-event', self.on_job_cancel_prompt_delete)
  878.         dialog.show_all()
  879.  
  880.     
  881.     def on_job_cancel_prompt_delete(self, dialog, event):
  882.         self.on_job_cancel_prompt_response(dialog, gtk.RESPONSE_NO)
  883.  
  884.     
  885.     def on_job_cancel_prompt_response(self, dialog, response):
  886.         jobid = dialog.get_data('job-id')
  887.         dialog.destroy()
  888.         if response != gtk.RESPONSE_YES:
  889.             return None
  890.         
  891.         try:
  892.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  893.         except RuntimeError:
  894.             response != gtk.RESPONSE_YES
  895.             response != gtk.RESPONSE_YES
  896.             return None
  897.  
  898.         c._begin_operation(_('canceling job'))
  899.         
  900.         try:
  901.             c.cancelJob(jobid)
  902.         except cups.IPPError:
  903.             response != gtk.RESPONSE_YES
  904.             (e, m) = response != gtk.RESPONSE_YES
  905.             if e != cups.IPP_NOT_POSSIBLE and e != cups.IPP_NOT_FOUND:
  906.                 self.show_IPP_Error(e, m)
  907.             
  908.             self.monitor.update()
  909.             c._end_operation()
  910.             return None
  911.  
  912.         c._end_operation()
  913.         del c
  914.         self.monitor.update()
  915.  
  916.     
  917.     def on_job_hold_activate(self, menuitem):
  918.         
  919.         try:
  920.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  921.         except RuntimeError:
  922.             return None
  923.  
  924.         c._begin_operation(_('holding job'))
  925.         
  926.         try:
  927.             c.setJobHoldUntil(self.jobid, 'indefinite')
  928.         except cups.IPPError:
  929.             (e, m) = None
  930.             if e != cups.IPP_NOT_POSSIBLE and e != cups.IPP_NOT_FOUND:
  931.                 self.show_IPP_Error(e, m)
  932.             
  933.             self.monitor.update()
  934.             c._end_operation()
  935.             return None
  936.  
  937.         c._end_operation()
  938.         del c
  939.         self.monitor.update()
  940.  
  941.     
  942.     def on_job_release_activate(self, menuitem):
  943.         
  944.         try:
  945.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  946.         except RuntimeError:
  947.             return None
  948.  
  949.         c._begin_operation(_('releasing job'))
  950.         
  951.         try:
  952.             c.setJobHoldUntil(self.jobid, 'no-hold')
  953.         except cups.IPPError:
  954.             (e, m) = None
  955.             if e != cups.IPP_NOT_POSSIBLE and e != cups.IPP_NOT_FOUND:
  956.                 self.show_IPP_Error(e, m)
  957.             
  958.             self.monitor.update()
  959.             c._end_operation()
  960.             return None
  961.  
  962.         c._end_operation()
  963.         del c
  964.         self.monitor.update()
  965.  
  966.     
  967.     def on_job_reprint_activate(self, menuitem):
  968.         
  969.         try:
  970.             c = authconn.Connection(self.JobsWindow, host = self.host, port = self.port, encryption = self.encryption)
  971.             c.restartJob(self.jobid)
  972.             del c
  973.         except cups.IPPError:
  974.             (e, m) = None
  975.             self.show_IPP_Error(e, m)
  976.             self.monitor.update()
  977.             return None
  978.             except RuntimeError:
  979.                 return None
  980.             else:
  981.                 self.monitor.update()
  982.                 return None
  983.  
  984.  
  985.     
  986.     def on_job_authenticate_activate(self, menuitem):
  987.         self.display_auth_info_dialog(self.jobid)
  988.  
  989.     
  990.     def on_refresh_activate(self, menuitem):
  991.         self.monitor.refresh()
  992.  
  993.     
  994.     def job_is_active(self, jobdata):
  995.         state = jobdata.get('job-state', cups.IPP_JOB_CANCELED)
  996.         if state >= cups.IPP_JOB_CANCELED:
  997.             return False
  998.         return True
  999.  
  1000.     
  1001.     def add_state_reason_emblem(self, pixbuf, printer = None):
  1002.         worst_reason = None
  1003.         if printer == None and self.worst_reason != None:
  1004.             printer = self.worst_reason.get_printer()
  1005.             found = False
  1006.             for reason in self.printer_state_reasons.get(printer, []):
  1007.                 if reason == self.worst_reason:
  1008.                     worst_reason = self.worst_reason
  1009.                     break
  1010.                     continue
  1011.             
  1012.             if worst_reason == None:
  1013.                 self.worst_reason = None
  1014.             
  1015.         
  1016.         if printer != None:
  1017.             for reason in self.printer_state_reasons.get(printer, []):
  1018.                 if worst_reason == None:
  1019.                     worst_reason = reason
  1020.                     continue
  1021.                 if reason > worst_reason:
  1022.                     worst_reason = reason
  1023.                     continue
  1024.             
  1025.         
  1026.         if worst_reason != None:
  1027.             level = worst_reason.get_level()
  1028.             if level > StateReason.REPORT:
  1029.                 icon = StateReason.LEVEL_ICON[level]
  1030.                 pixbuf = pixbuf.copy()
  1031.                 theme = gtk.icon_theme_get_default()
  1032.                 emblem = theme.load_icon(icon, 22, 0)
  1033.                 emblem.composite(pixbuf, pixbuf.get_width() / 2, pixbuf.get_height() / 2, emblem.get_width() / 2, emblem.get_height() / 2, pixbuf.get_width() / 2, pixbuf.get_height() / 2, 0.5, 0.5, gtk.gdk.INTERP_BILINEAR, 255)
  1034.             
  1035.         
  1036.         return pixbuf
  1037.  
  1038.     
  1039.     def get_icon_pixbuf(self, have_jobs = None):
  1040.         if not self.trayicon:
  1041.             return None
  1042.         if have_jobs == None:
  1043.             have_jobs = len(self.jobs.keys()) > 0
  1044.         
  1045.         if have_jobs:
  1046.             pixbuf = self.icon_jobs
  1047.             for jobid, jobdata in self.jobs.iteritems():
  1048.                 jstate = jobdata.get('job-state', cups.IPP_JOB_PENDING)
  1049.                 if jstate == cups.IPP_JOB_PROCESSING:
  1050.                     pixbuf = self.icon_jobs_processing
  1051.                     break
  1052.                     continue
  1053.             
  1054.         else:
  1055.             pixbuf = self.icon_no_jobs
  1056.         
  1057.         try:
  1058.             pixbuf = self.add_state_reason_emblem(pixbuf)
  1059.         except:
  1060.             nonfatalException()
  1061.  
  1062.         return pixbuf
  1063.  
  1064.     
  1065.     def set_statusicon_tooltip(self, tooltip = None):
  1066.         if not self.trayicon:
  1067.             return None
  1068.         if tooltip == None:
  1069.             num_jobs = len(self.jobs)
  1070.             if num_jobs == 0:
  1071.                 tooltip = _('No documents queued')
  1072.             elif num_jobs == 1:
  1073.                 tooltip = _('1 document queued')
  1074.             else:
  1075.                 tooltip = _('%d documents queued') % num_jobs
  1076.         
  1077.         self.statusicon.set_tooltip(tooltip)
  1078.  
  1079.     
  1080.     def update_status(self, have_jobs = None):
  1081.         upset_printers = set()
  1082.         for printer, reasons in self.printer_state_reasons.iteritems():
  1083.             if len(reasons) > 0:
  1084.                 upset_printers.add(printer)
  1085.                 continue
  1086.         
  1087.         debugprint('Upset printers: %s' % upset_printers)
  1088.         my_upset_printers = set()
  1089.         if len(upset_printers):
  1090.             my_upset_printers = set()
  1091.             for jobid in self.active_jobs:
  1092.                 printer = self.jobs[jobid]['job-printer-name']
  1093.                 if printer in upset_printers:
  1094.                     my_upset_printers.add(printer)
  1095.                     continue
  1096.             
  1097.             debugprint('My upset printers: %s' % my_upset_printers)
  1098.         
  1099.         my_reasons = []
  1100.         for printer in my_upset_printers:
  1101.             my_reasons.extend(self.printer_state_reasons[printer])
  1102.         
  1103.         self.worst_reason = None
  1104.         if len(my_reasons) > 0:
  1105.             worst_reason = my_reasons[0]
  1106.             for reason in my_reasons:
  1107.                 if reason > worst_reason:
  1108.                     worst_reason = reason
  1109.                     continue
  1110.             
  1111.             self.worst_reason = worst_reason
  1112.             debugprint('Worst reason: %s' % worst_reason)
  1113.         
  1114.         if self.worst_reason != None:
  1115.             (title, tooltip) = self.worst_reason.get_description()
  1116.             if self.statusbar_set:
  1117.                 self.statusbar.pop(0)
  1118.             
  1119.             self.statusbar.push(0, tooltip)
  1120.             self.statusbar_set = True
  1121.         else:
  1122.             tooltip = None
  1123.             if self.statusbar_set:
  1124.                 self.statusbar.pop(0)
  1125.                 self.statusbar_set = False
  1126.             
  1127.         if self.trayicon:
  1128.             pixbuf = self.get_icon_pixbuf(have_jobs = have_jobs)
  1129.             self.set_statusicon_from_pixbuf(pixbuf)
  1130.             self.set_statusicon_visibility()
  1131.             self.set_statusicon_tooltip(tooltip = tooltip)
  1132.         
  1133.  
  1134.     
  1135.     def notify_printer_state_reason_if_important(self, reason):
  1136.         level = reason.get_level()
  1137.         if level < StateReason.WARNING:
  1138.             return None
  1139.         self.notify_printer_state_reason(reason)
  1140.  
  1141.     
  1142.     def notify_printer_state_reason(self, reason):
  1143.         tuple = reason.get_tuple()
  1144.         if self.state_reason_notifications.has_key(tuple):
  1145.             debugprint('Already sent notification for %s' % repr(reason))
  1146.             return None
  1147.         level = reason.get_level()
  1148.         if level == StateReason.ERROR or reason.get_reason() == 'connecting-to-device':
  1149.             urgency = pynotify.URGENCY_NORMAL
  1150.         else:
  1151.             urgency = pynotify.URGENCY_LOW
  1152.         (title, text) = reason.get_description()
  1153.         notification = pynotify.Notification(title, text, 'printer')
  1154.         reason.user_notified = True
  1155.         notification.set_urgency(urgency)
  1156.         if 'actions' in pynotify.get_server_caps():
  1157.             notification.set_timeout(pynotify.EXPIRES_NEVER)
  1158.         
  1159.         notification.connect('closed', self.on_state_reason_notification_closed)
  1160.         self.state_reason_notifications[reason.get_tuple()] = notification
  1161.         self.set_statusicon_visibility()
  1162.         notification.attach_to_status_icon(self.statusicon)
  1163.         
  1164.         try:
  1165.             notification.show()
  1166.         except gobject.GError:
  1167.             nonfatalException()
  1168.  
  1169.  
  1170.     
  1171.     def on_state_reason_notification_closed(self, notification, reason = None):
  1172.         debugprint('Notification %s closed' % repr(notification))
  1173.         notification.set_data('closed', True)
  1174.         self.set_statusicon_visibility()
  1175.  
  1176.     
  1177.     def current_printers_and_jobs(self, mon, printers, jobs):
  1178.         monitor.Watcher.current_printers_and_jobs(self, mon, printers, jobs)
  1179.         self.set_process_pending(False)
  1180.         self.store.clear()
  1181.         self.jobs = { }
  1182.         self.jobiters = { }
  1183.         self.printer_uri_index = PrinterURIIndex(names = printers)
  1184.         connection = None
  1185.         for jobid, jobdata in jobs.iteritems():
  1186.             uri = jobdata.get('job-printer-uri', '')
  1187.             
  1188.             try:
  1189.                 printer = self.printer_uri_index.lookup(uri, connection = connection)
  1190.             except KeyError:
  1191.                 printer = uri
  1192.  
  1193.             jobdata['job-printer-name'] = printer
  1194.             self.add_job(jobid, jobdata, connection = connection)
  1195.         
  1196.         self.jobs = jobs
  1197.         self.active_jobs = set()
  1198.         for jobid, jobdata in jobs.iteritems():
  1199.             if self.job_is_active(jobdata):
  1200.                 self.active_jobs.add(jobid)
  1201.                 continue
  1202.         
  1203.         self.set_process_pending(True)
  1204.         self.update_status()
  1205.  
  1206.     
  1207.     def job_added(self, mon, jobid, eventname, event, jobdata):
  1208.         monitor.Watcher.job_added(self, mon, jobid, eventname, event, jobdata)
  1209.         uri = jobdata.get('job-printer-uri', '')
  1210.         
  1211.         try:
  1212.             printer = self.printer_uri_index.lookup(uri)
  1213.         except KeyError:
  1214.             printer = uri
  1215.  
  1216.         jobdata['job-printer-name'] = printer
  1217.         if not self.jobiters.has_key(jobid):
  1218.             self.add_job(jobid, jobdata)
  1219.         
  1220.         if self.job_is_active(jobdata):
  1221.             self.active_jobs.add(jobid)
  1222.         elif jobid in self.active_jobs:
  1223.             self.active_jobs.remove(jobid)
  1224.         
  1225.         self.update_status(have_jobs = True)
  1226.         if self.trayicon:
  1227.             if not self.job_is_active(jobdata):
  1228.                 return None
  1229.             for reason in self.printer_state_reasons.get(printer, []):
  1230.                 if not reason.user_notified:
  1231.                     self.notify_printer_state_reason_if_important(reason)
  1232.                     continue
  1233.                 self.job_is_active(jobdata)
  1234.             
  1235.         
  1236.  
  1237.     
  1238.     def job_event(self, mon, jobid, eventname, event, jobdata):
  1239.         monitor.Watcher.job_event(self, mon, jobid, eventname, event, jobdata)
  1240.         uri = jobdata.get('job-printer-uri', '')
  1241.         
  1242.         try:
  1243.             printer = self.printer_uri_index.lookup(uri)
  1244.         except KeyError:
  1245.             printer = uri
  1246.  
  1247.         jobdata['job-printer-name'] = printer
  1248.         if self.job_is_active(jobdata):
  1249.             self.active_jobs.add(jobid)
  1250.         elif jobid in self.active_jobs:
  1251.             self.active_jobs.remove(jobid)
  1252.         
  1253.         self.update_job(jobid, jobdata)
  1254.         self.update_status()
  1255.         jobdata = self.jobs[jobid]
  1256.         if self.trayicon:
  1257.             if (eventname == 'job-stopped' or eventname == 'job-state-changed' or event['job-state'] in [
  1258.                 cups.IPP_JOB_STOPPED,
  1259.                 cups.IPP_JOB_PENDING]) and jobid not in self.stopped_job_prompts:
  1260.                 may_be_problem = True
  1261.                 jstate = jobdata['job-state']
  1262.                 if (jstate == cups.IPP_JOB_PROCESSING or jstate == cups.IPP_JOB_HELD) and jobdata['job-hold-until'] == 'auth-info-required':
  1263.                     may_be_problem = False
  1264.                 else:
  1265.                     notify_text = event['notify-text']
  1266.                     document = jobdata['job-name']
  1267.                     if notify_text.find('backend errors') != -1:
  1268.                         message = _("There was a problem sending document `%s' (job %d) to the printer.") % (document, jobid)
  1269.                     elif notify_text.find('filter errors') != -1:
  1270.                         message = _("There was a problem processing document `%s' (job %d).") % (document, jobid)
  1271.                     elif notify_text.find('being paused') != -1 or jstate != cups.IPP_JOB_STOPPED:
  1272.                         may_be_problem = False
  1273.                     else:
  1274.                         message = _("There was a problem printing document `%s' (job %d): `%s'.") % (document, jobid, notify_text)
  1275.                 if may_be_problem:
  1276.                     debugprint('Problem detected')
  1277.                     self.toggle_window_display(self.statusicon, force_show = True)
  1278.                     dialog = gtk.Dialog(_('Print Error'), self.JobsWindow, 0, (_('_Diagnose'), gtk.RESPONSE_NO, gtk.STOCK_OK, gtk.RESPONSE_OK))
  1279.                     dialog.set_default_response(gtk.RESPONSE_OK)
  1280.                     dialog.set_border_width(6)
  1281.                     dialog.set_resizable(False)
  1282.                     dialog.set_icon_name(ICON)
  1283.                     hbox = gtk.HBox(False, 12)
  1284.                     hbox.set_border_width(6)
  1285.                     image = gtk.Image()
  1286.                     image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
  1287.                     hbox.pack_start(image, False, False, 0)
  1288.                     vbox = gtk.VBox(False, 12)
  1289.                     markup = '<span weight="bold" size="larger">' + _('Print Error') + '</span>\n\n' + message
  1290.                     
  1291.                     try:
  1292.                         if event['printer-state'] == cups.IPP_PRINTER_STOPPED:
  1293.                             name = event['printer-name']
  1294.                             markup += ' '
  1295.                             markup += _("The printer called `%s' has been disabled.") % name
  1296.                     except KeyError:
  1297.                         pass
  1298.  
  1299.                     label = gtk.Label(markup)
  1300.                     label.set_use_markup(True)
  1301.                     label.set_line_wrap(True)
  1302.                     label.set_alignment(0, 0)
  1303.                     vbox.pack_start(label, False, False, 0)
  1304.                     hbox.pack_start(vbox, False, False, 0)
  1305.                     dialog.vbox.pack_start(hbox)
  1306.                     dialog.connect('response', self.print_error_dialog_response, jobid)
  1307.                     self.stopped_job_prompts.add(jobid)
  1308.                     dialog.show_all()
  1309.                 
  1310.             
  1311.  
  1312.     
  1313.     def job_removed(self, mon, jobid, eventname, event):
  1314.         monitor.Watcher.job_removed(self, mon, jobid, eventname, event)
  1315.         if self.jobiters.has_key(jobid):
  1316.             self.store.remove(self.jobiters[jobid])
  1317.             del self.jobiters[jobid]
  1318.             del self.jobs[jobid]
  1319.         
  1320.         if jobid in self.active_jobs:
  1321.             self.active_jobs.remove(jobid)
  1322.         
  1323.         self.update_status()
  1324.  
  1325.     
  1326.     def state_reason_added(self, mon, reason):
  1327.         monitor.Watcher.state_reason_added(self, mon, reason)
  1328.         (title, text) = reason.get_description()
  1329.         printer = reason.get_printer()
  1330.         
  1331.         try:
  1332.             l = self.printer_state_reasons[printer]
  1333.         except KeyError:
  1334.             l = []
  1335.             self.printer_state_reasons[printer] = l
  1336.  
  1337.         reason.user_notified = False
  1338.         l.append(reason)
  1339.         self.update_status()
  1340.         self.treeview.queue_draw()
  1341.         if not self.trayicon:
  1342.             return None
  1343.         for job, data in self.jobs.iteritems():
  1344.             if not self.job_is_active(data):
  1345.                 continue
  1346.             
  1347.             if data['job-printer-name'] == printer:
  1348.                 self.notify_printer_state_reason_if_important(reason)
  1349.                 break
  1350.                 continue
  1351.         
  1352.  
  1353.     
  1354.     def state_reason_removed(self, mon, reason):
  1355.         monitor.Watcher.state_reason_removed(self, mon, reason)
  1356.         printer = reason.get_printer()
  1357.         
  1358.         try:
  1359.             reasons = self.printer_state_reasons[printer]
  1360.         except KeyError:
  1361.             debugprint('Printer not found')
  1362.             return None
  1363.  
  1364.         
  1365.         try:
  1366.             i = reasons.index(reason)
  1367.         except IndexError:
  1368.             debugprint('Reason not found')
  1369.             return None
  1370.  
  1371.         del reasons[i]
  1372.         self.update_status()
  1373.         self.treeview.queue_draw()
  1374.         if not self.trayicon:
  1375.             return None
  1376.         tuple = reason.get_tuple()
  1377.         
  1378.         try:
  1379.             notification = self.state_reason_notifications[tuple]
  1380.             if notification.get_data('closed') != True:
  1381.                 notification.close()
  1382.             
  1383.             del self.state_reason_notifications[tuple]
  1384.             self.set_statusicon_visibility()
  1385.         except KeyError:
  1386.             self.trayicon
  1387.             self.trayicon
  1388.         except:
  1389.             self.trayicon
  1390.  
  1391.  
  1392.     
  1393.     def still_connecting(self, mon, reason):
  1394.         monitor.Watcher.still_connecting(self, mon, reason)
  1395.         if not self.trayicon:
  1396.             return None
  1397.         self.notify_printer_state_reason(reason)
  1398.  
  1399.     
  1400.     def now_connected(self, mon, printer):
  1401.         monitor.Watcher.now_connected(self, mon, printer)
  1402.         if not self.trayicon:
  1403.             return None
  1404.         
  1405.         try:
  1406.             reasons = self.printer_state_reasons[printer]
  1407.             reason = None
  1408.             for r in reasons:
  1409.                 if r.get_reason() == 'connecting-to-device':
  1410.                     reason = r
  1411.                     break
  1412.                     continue
  1413.                 self.trayicon
  1414.         except KeyError:
  1415.             self.trayicon
  1416.             self.trayicon
  1417.             debugprint("Couldn't find state reason (no reasons)!")
  1418.         except:
  1419.             self.trayicon
  1420.  
  1421.         if reason != None:
  1422.             tuple = reason.get_tuple()
  1423.         else:
  1424.             debugprint("Couldn't find state reason in list!")
  1425.             for level, p, r in self.state_reason_notifications.keys():
  1426.                 if p == printer and r == 'connecting-to-device':
  1427.                     debugprint('Found from notifications list')
  1428.                     tuple = (level, p, r)
  1429.                     break
  1430.                     continue
  1431.             
  1432.         
  1433.         try:
  1434.             notification = self.state_reason_notifications[tuple]
  1435.         except KeyError:
  1436.             debugprint('Unexpected now_connected signal')
  1437.             return None
  1438.  
  1439.         if notification.get_data('closed') != True:
  1440.             notification.close()
  1441.             notification.set_data('closed', True)
  1442.         
  1443.  
  1444.     
  1445.     def printer_event(self, mon, printer, eventname, event):
  1446.         monitor.Watcher.printer_event(self, mon, printer, eventname, event)
  1447.         self.printer_uri_index.update_from_attrs(printer, event)
  1448.  
  1449.     
  1450.     def printer_removed(self, mon, printer):
  1451.         monitor.Watcher.printer_removed(self, mon, printer)
  1452.         self.printer_uri_index.remove_printer(printer)
  1453.  
  1454.     
  1455.     def _set_job_job_number_text(self, column, cell, model, iter, *data):
  1456.         cell.set_property('text', str(model.get_value(iter, 0)))
  1457.  
  1458.     
  1459.     def _set_job_user_text(self, column, cell, model, iter, *data):
  1460.         jobid = model.get_value(iter, 0)
  1461.         job = self.jobs[jobid]
  1462.         cell.set_property('text', job.get('job-originating-user-name', _('Unknown')))
  1463.  
  1464.     
  1465.     def _set_job_document_text(self, column, cell, model, iter, *data):
  1466.         jobid = model.get_value(iter, 0)
  1467.         job = self.jobs[jobid]
  1468.         cell.set_property('text', job.get('job-name', _('Unknown')))
  1469.  
  1470.     
  1471.     def _set_job_printer_text(self, column, cell, model, iter, *data):
  1472.         jobid = model.get_value(iter, 0)
  1473.         cell.set_property('text', self.jobs[jobid]['job-printer-name'])
  1474.  
  1475.     
  1476.     def _set_job_size_text(self, column, cell, model, iter, *data):
  1477.         jobid = model.get_value(iter, 0)
  1478.         job = self.jobs[jobid]
  1479.         size = _('Unknown')
  1480.         if job.has_key('job-k-octets'):
  1481.             size = str(job['job-k-octets']) + 'k'
  1482.         
  1483.         cell.set_property('text', size)
  1484.  
  1485.     
  1486.     def _find_job_state_text(self, job):
  1487.         data = self.jobs[job]
  1488.         jstate = data.get('job-state', cups.IPP_JOB_PROCESSING)
  1489.         s = int(jstate)
  1490.         if s == cups.IPP_JOB_HELD:
  1491.             pass
  1492.         job_requires_auth = data.get('job-hold-until', 'none') == 'auth-info-required'
  1493.         state = None
  1494.         if job_requires_auth:
  1495.             state = _('Held for authentication')
  1496.         elif s == cups.IPP_JOB_HELD:
  1497.             state = _('Held')
  1498.             until = data.get('job-hold-until')
  1499.             if until != None:
  1500.                 
  1501.                 try:
  1502.                     colon1 = until.find(':')
  1503.                     if colon1 != -1:
  1504.                         now = time.gmtime()
  1505.                         hh = int(until[:colon1])
  1506.                         colon2 = until[colon1 + 1:].find(':')
  1507.                         if colon2 != -1:
  1508.                             colon2 += colon1 + 1
  1509.                             mm = int(until[colon1 + 1:colon2])
  1510.                             ss = int(until[colon2 + 1:])
  1511.                         else:
  1512.                             mm = int(until[colon1 + 1:])
  1513.                             ss = 0
  1514.                         day = now.tm_mday
  1515.                         if hh < now.tm_hour or hh == now.tm_hour:
  1516.                             if (mm < now.tm_min or mm == now.tm_min) and ss < now.tm_sec:
  1517.                                 day += 1
  1518.                             
  1519.                         hold = (now.tm_year, now.tm_mon, day, hh, mm, ss, 0, 0, -1)
  1520.                         old_tz = os.environ.get('TZ')
  1521.                         os.environ['TZ'] = 'UTC'
  1522.                         simpletime = time.mktime(hold)
  1523.                         if old_tz == None:
  1524.                             del os.environ['TZ']
  1525.                         else:
  1526.                             os.environ['TZ'] = old_tz
  1527.                         local = time.localtime(simpletime)
  1528.                         state = _('Held until %s') % time.strftime('%X', local)
  1529.                 except ValueError:
  1530.                     pass
  1531.                 except:
  1532.                     None<EXCEPTION MATCH>ValueError
  1533.                 
  1534.  
  1535.             None<EXCEPTION MATCH>ValueError
  1536.             if until == 'day-time':
  1537.                 state = _('Held until day-time')
  1538.             elif until == 'evening':
  1539.                 state = _('Held until evening')
  1540.             elif until == 'night':
  1541.                 state = _('Held until night-time')
  1542.             elif until == 'second-shift':
  1543.                 state = _('Held until second shift')
  1544.             elif until == 'third-shift':
  1545.                 state = _('Held until third shift')
  1546.             elif until == 'weekend':
  1547.                 state = _('Held until weekend')
  1548.             
  1549.         else:
  1550.             
  1551.             try:
  1552.                 state = {
  1553.                     cups.IPP_JOB_PENDING: _('Pending'),
  1554.                     cups.IPP_JOB_PROCESSING: _('Processing'),
  1555.                     cups.IPP_JOB_STOPPED: _('Stopped'),
  1556.                     cups.IPP_JOB_CANCELED: _('Canceled'),
  1557.                     cups.IPP_JOB_ABORTED: _('Aborted'),
  1558.                     cups.IPP_JOB_COMPLETED: _('Completed') }[s]
  1559.             except IndexError:
  1560.                 pass
  1561.  
  1562.         if state == None:
  1563.             state = _('Unknown')
  1564.         
  1565.         return state
  1566.  
  1567.     
  1568.     def _set_job_status_icon(self, column, cell, model, iter, *data):
  1569.         jobid = model.get_value(iter, 0)
  1570.         data = self.jobs[jobid]
  1571.         jstate = data.get('job-state', cups.IPP_JOB_PROCESSING)
  1572.         s = int(jstate)
  1573.         if s == cups.IPP_JOB_PROCESSING:
  1574.             icon = self.icon_jobs_processing
  1575.         else:
  1576.             icon = self.icon_jobs
  1577.         if s == cups.IPP_JOB_HELD:
  1578.             theme = gtk.icon_theme_get_default()
  1579.             emblem = theme.load_icon(gtk.STOCK_MEDIA_PAUSE, 22 / 2, 0)
  1580.             copy = icon.copy()
  1581.             emblem.composite(copy, 0, 0, copy.get_width(), copy.get_height(), copy.get_width() / 2 - 1, copy.get_height() / 2 - 1, 1, 1, gtk.gdk.INTERP_NEAREST, 255)
  1582.             icon = copy
  1583.         else:
  1584.             printer = data['job-printer-name']
  1585.             icon = self.add_state_reason_emblem(icon, printer = printer)
  1586.         cell.set_property('pixbuf', icon)
  1587.  
  1588.     
  1589.     def _set_job_status_text(self, column, cell, model, iter, *data):
  1590.         jobid = model.get_value(iter, 0)
  1591.         data = self.jobs[jobid]
  1592.         
  1593.         try:
  1594.             text = data['_status_text']
  1595.         except KeyError:
  1596.             text = self._find_job_state_text(jobid)
  1597.             data['_status_text'] = text
  1598.  
  1599.         printer = data['job-printer-name']
  1600.         reasons = self.printer_state_reasons.get(printer, [])
  1601.         if len(reasons) > 0:
  1602.             worst_reason = reasons[0]
  1603.             for reason in reasons[1:]:
  1604.                 if reason > worst_reason:
  1605.                     worst_reason = reason
  1606.                     continue
  1607.             
  1608.             (title, unused) = worst_reason.get_description()
  1609.             text += ' - ' + title
  1610.         
  1611.         cell.set_property('text', text)
  1612.  
  1613.  
  1614.